###
 # Class for doing interractions with the forex club's web server.
 # 
 # (c) 2007, MDE, dmitry.ema@gmail.com
 #
import sys, time

try :
  import httplib2
except Exception,e:
  print "Module 'httplib2' wasn't found. Please install this module from http://code.google.com/p/httplib2/"
  sys.exit()

PROXY_ENABLE = True

try :
  import socks
except Exception,e:
  PROXY_ENABLE = False
  print """Module 'socks' wasn't found. You can't work via proxy.
           For enable that you should install this module from http://socksipy.sourceforge.net/
        """

from Screen     import *
from FxParser   import *
from Structures import *

STATUS_OK  = 302
LOCAL_DIR  = "local"
BASE_URL   = "https://webtrade1.fxclub.com/"
QUOTES_URL = "http://www.fxclub.org/module/rss/"
QUOTE_SRV_URL = "http://quotesrv.fxclub.org/"
TOOLS_CFG  = "tools.cfg"

## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 # Just interface for the future improvements for using another
 # trade's API
 #
class AbstractDriver:
  def readToolsCfg(this):
	f = file(TOOLS_CFG)
 	this.tools = {}
 	for i in f.readlines():
 	  x = map(string.strip,i.split(","))
	  t = Tool(x)
  	  this.tools[t.name] = t
	f.close()
  
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 # Driver for interactions with FX club's http server
 #
class FxClubWebDriver(AbstractDriver):
  
  def __init__(this,isDbg,isLocal,useProxy=False,proxyServer='localhost',proxyPort=8080):
	this.isDbg = isDbg
	this.USE_HTTP = not isLocal
	isHprx = False

	if useProxy:
	  if PROXY_ENABLE:
 		this.hPrx = httplib2.Http(".cache",proxy_info = httplib2.ProxyInfo(socks.PROXY_TYPE_HTTP,proxyServer,proxyPort))
		this.dbgPrint("Using proxy %s:%s" % (proxyServer,proxyPort))
		isHprx = True
	  else:
		this.err("You can't use proxy server because some modules are not installed on your system")

	# create new http instance
	this.h = httplib2.Http(".cache")
	if not isHprx: this.hPrx = this.h

	# create html parser instance
	this.parser = FxParser()
	this.cookie = None

	# read tools configuration
	this.readToolsCfg()

 
  def _send_get_req(this,uri,baseUrl=BASE_URL,useProxy=False):
	if this.USE_HTTP:
	  h = this.h
	  if useProxy: h = this.hPrx
	  r,content = h.request(baseUrl + uri,'GET',headers=this.cookie)
	else:
	  if uri=='/': uri = 'root'
	  f = file(LOCAL_DIR + "/" + uri + ".html")
	  content = string.join(f.readlines())
	  r = httplib2.Response({"status":str(STATUS_OK)})
	  r['set-cookie'] = "x"
	  r.status = 200
	return r,content
  
  
  def _send_post_req(this,uri,bd,baseUrl=BASE_URL):
	if this.USE_HTTP:
	  hdr = {'Content-Type': 'application/x-www-form-urlencoded'}
	  if this.cookie != None:
		hdr['Cookie'] = this.cookie['Cookie']
	  r,content = this.h.request(baseUrl + uri,'POST',body=bd, headers=hdr)
	else:
	  if uri=='/': uri = 'root'
	  f = file(LOCAL_DIR + "/" + uri + ".html")
	  content = string.join(f.readlines())
	  r = httplib2.Response({"status":str(STATUS_OK)})
	  r['set-cookie'] = "x"
	return r,content


  # get tool by it's alias
  def getToolByAlias(this,name):
	for i in this.tools:
	  t = this.tools[i]
	  if t.hasAliase(name): return t
	return None

  
  # logout
  def logout(this):
	this.dbgPrint("try to logout ...")
	try:
	  r, content = this._send_get_req("logout")
	except Exception,e:
	  this.err("Error logout from ",BASE_URL,e)
	this.dbgPrint("done")
	

  # Tries to login and starts session
  def login(this,uLogin,uPass):
	this.uLogin = uLogin
	this.uPass = uPass
	this.dbgPrint("try to login ...")
	authRequest = "login=%s&password=%s" % (uLogin,uPass)
	try:
	  r, content = this._send_post_req("login",authRequest)
	except Exception,e:
	  this.err("Error connecting to ",BASE_URL,e)
	  return False
	this.dbgPrint("result: ",r.status)
	if r.has_key('set-cookie'):
	  this.cookie = {'Cookie': r['set-cookie']}
	  #print r['set-cookie']
	return r.status == STATUS_OK

  
  # Retrieves current balance
  # returns balance as float
  def getBalance(this):
	r, content = this._send_get_req("/")
 	this.parser.parseDocumentString(content)
	this.account = this.parser.getFollowedNode('TH','Account:','TD')
	return float(this.parser.getFollowedNode('TH','Balance','TD'))


  # Returns id of current account
  # as String
  def getAccount(this):
	return this.account


  # This method gets orders from the server
  # parses and returns them as list of 'Order' objects
  def getOrders(this):
	orders = []
	r, content = this._send_get_req("orders")
	p = this.parser
 	p.parseDocumentString(content)
 	e = p.doc.documentElement.getElementsByTagName('TABLE')
	for x in e:
	  if p.getElementAttribute(x,'class') == 'grid':
		# skip table header
		for y in x.getElementsByTagName('TR')[1:]:
		  state = p.getElementAttribute(y,'class')
		  if state!='inactive': state = 'active'
		  
		  oElements = y.getElementsByTagName('TD')
		  if len(oElements) < 5: return orders
		  clearLink = p.getElementAttribute(oElements[5].getElementsByTagName('A')[0],'href')

		  _x = map(lambda t:t.firstChild.nodeValue,oElements[1:5])
 		  orders.append(Order(state,clearLink,_x))
  
		break
	return orders

  
  # This method gets open positions from the server
  # parses and returns them as list (Oops, just prints them)
  def getPositions(this):
	poses = []
	r, content = this._send_get_req("positions")
	p = this.parser
 	p.parseDocumentString(content)
 	e = p.doc.documentElement.getElementsByTagName('TABLE')
	for x in e:
	  if p.getElementAttribute(x,'class') == 'grid':
		for y in x.getElementsByTagName('TR'):
		  oElements = y.getElementsByTagName('TD')
		  
		  # how much elements are there
		  if len(oElements)<5: continue
		  _x = map(lambda t:t.firstChild.nodeValue,oElements[0:5])
		  if len(_x)==0: continue
		  t = this.getToolByAlias(_x[0])
		  if t!=None:
			p = Position(t,_x)
			p.setNumber(len(poses)+1)
			poses.append(p)
		  else:
			this.err('Tool ',_x[0],' isn\'t defined')
		  #print string.join(_x,'\t')
		break
	return poses


  # Core method for make deal
  def _doDeal(this,type,curr,lot):
	# - step 1: send request for current price for this tool
	req = "currency=%s&lot=%s"  % (curr,"%.3f" % lot)

	# send request for buy
	print "try to %s %.3f of '%s'" % (type,lot,curr)
	r, content = this._send_post_req("deal",req)

	# check for null answer (it's happened in off time)
# 	if len(content) == 0:
# 	  this.err("Empty answer")
# 	  return (False,"Empty answer")

	# check answer status
	if r.status != STATUS_OK:
	  this.parser.parseDocumentString(content)
	  errCause = this.parser.getFollowedNode('H4','Deal failed','BR')
	  msgCause = this.parser.getFollowedNode('H4','Message','BR')
	  if errCause!=None and len(errCause)>0: errCause = errCause.strip()
	  if msgCause!=None and len(msgCause)>0: msgCause = msgCause.strip()
	  this.err(errCause or msgCause)
	  return (False,errCause or msgCause)
	  
	# - step 2: try to get current price
	print "getting current price ..."
	r, content = this._send_get_req("deal_price")
	if r.status != 200:
	  this.parser.parseDocumentString(content)
	  errCause = this.parser.getFollowedNode('H4','Deal failed','BR')
	  msgCause = this.parser.getFollowedNode('H4','Message','BR')
	  if errCause!=None and len(errCause)>0: errCause = errCause.strip()
	  if msgCause!=None and len(msgCause)>0: msgCause = msgCause.strip()
	  this.err(errCause or msgCause)
	  return (False,errCause or msgCause)

	# - step 3: and finally we complete the deal
	r, content = this._send_get_req("deal_complete/go/%s" % type)
	if r.status != 200:
	  this.parser.parseDocumentString(content)
	  errCause = this.parser.getFollowedNode('H4','Deal failed','BR')
	  msgCause = this.parser.getFollowedNode('H4','Message','BR')
	  if errCause!=None and len(errCause)>0: errCause = errCause.strip()
	  if msgCause!=None and len(msgCause)>0: msgCause = msgCause.strip()
	  this.err(errCause or msgCause)
	  return (False,errCause or msgCause)

	# print summary info: everything is fine
	print WHITE,
	if type == 'buy': print 'bought',
	else: print 'sold',
	print " %.3f of '%s'" % (lot,curr)
	print GRAY,
	return (True,'OK')

  
  # Make a selling
  def doSell(this,tool,lot):
	t = this.getToolByAlias(tool)
	if t==None:
	  this.err('Tool ',tool,' isn\'t defined')
	  return
	return this._doDeal('sell',t.name,lot)
	  

  # Make a buying
  def doBuy(this,tool,lot):
	t = this.getToolByAlias(tool)
	if t==None:
	  this.err('Tool ',tool,' isn\'t defined')
	  return
	return this._doDeal('buy',t.name,lot)


  # Get current quotes
#   def getQuotes_OLD_APPROACH(this):
# 	res = {}
# 	try :
#  	  r,c = this._send_get_req("rss_quote.php",QUOTES_URL,useProxy=True)
# 	  # parse as xml document
# 	  this.parser.xmlParseDocumentString(c)

# 	  # get quotes for every tool that is described in tools.cfg
# 	  for i in this.tools.values():
# 		n = i.getCanonicalName()
# 		if n!=None and len(n)>0:
# 		  res[n] = this.parser.getFollowedNode('title',n,'description').strip()
		  
# 	except Exception,e:
# 	  this.err('getQuotes: ' + str(e))
	  
# 	return res

  # Get current quotes: from quotes server
  def getQuotes(this):
	res = {}
	try :
 	  r,c = this._send_get_req("/tools/quotes/data8/WebQuote13b.txt",QUOTE_SRV_URL,useProxy=True)
	  if r.status != 200:
		this.err('Couldn\'t get quotes from quotesrv')
		return res
	  
	  # content is plain txt file
	  c = c.split('=')[1].splitlines()
	  for i in c:
		data = [x for x in i.strip().split(' ') if x!='']
		res[data[0]] = [data[1],data[2],'']  # [bid,ask,state]
		
	except Exception,e:
	  this.err('getQuotes: ' + str(e))
	  
	return res
  

  # TODO: Set simple order
  def setOrder(this,tool,price,lot):
	buysell = 'buy'
	if lot < 0: buysell = 'sell'
	type = 1
	
	# - step 1
	req = "currency=%s&type=%s"  % (tool,type)

	# send request
	r, content = this._send_post_req("new_order",req)
	if r.status != STATUS_OK:
	  this.parser.parseDocumentString(content)
	  errCause = this.parser.getFollowedNode('H4','Deal failed','BR')
	  if errCause[0] == '\n': errCause = errCause[1:]
	  this.err(errCause)
	  return (False,errCause)
	
	
	  
  def dbgPrint(this,*args):
	if this.isDbg:
	  print MAGENTA,"DEBUG:",DARKGREEN,
	  for i in args: print i,
	  print GRAY

	  
  def warn(this,*args):
	print YELLOW,"WARN:",GREEN,
	for i in args: print i,
	print GRAY

	
  def err(this,*args):
	print RED,"ERROR:",WHITE,
	for i in args: print i,
	print GRAY

  
